using System;
using System.Collections.Generic;
using System.Text;
using ICSharpCode.TextEditor.Gui.CompletionWindow;
using ICSharpCode.TextEditor;
using System.Collections;
using System.Xml.Schema;
using System.Windows.Forms;
using System.Xml;
using System.IO;
using System.Windows.Threading;
using System.Diagnostics;
using System.Threading;

namespace ImmlPad.CodeCompletion
{
    public class XmlCompletionDataProvider : ICompletionDataProvider
    {        
        protected string preSelection = null;

        public static bool IsSchemaLoaded
        {
            get { return (defaultSchemaCompletionData != null); }
        }

        static XmlSchemaCompletionData defaultSchemaCompletionData = null;


        string defaultNamespacePrefix = String.Empty;

        public static void LoadSchema(string filename)
        {
            ParameterizedThreadStart p = new ParameterizedThreadStart(LoadSchemaFromFile);

            Thread t = new Thread(p);
            t.Priority = ThreadPriority.Lowest;
            t.IsBackground = true;
            t.Start(filename);
        }

        private static void LoadSchemaFromFile(object param)
        {
            string filename = param as string;

            if (filename != null && System.IO.File.Exists(filename))
            {
                defaultSchemaCompletionData = new XmlSchemaCompletionData(filename);
            }
            else
            {
                MessageBox.Show("Code completion scheme load failed.");
            }
        }

        #region ICompletionDataProvider Members

        public int DefaultIndex
        {
            get { return 0; }
        }

        public ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped)
        {
            string text = String.Concat(textArea.Document.GetText(0, textArea.Caret.Offset), charTyped);

            switch (charTyped)
            {
                case '<':
                    // Child element intellisense.
                    XmlElementPath parentPath = XmlParser.GetParentElementPath(text);
                    if (parentPath.Elements.Count > 0)
                    {                        
                        ICompletionData[] data = GetChildElementCompletionData(parentPath);
                        //returnval = data;
                        return data;
                    }
                    else if (defaultSchemaCompletionData != null)
                    {
                        return defaultSchemaCompletionData.GetElementCompletionData(defaultNamespacePrefix);
                    }
                    break;

                case ' ':
                    // Attribute intellisense.
                    if (!XmlParser.IsInsideAttributeValue(text, text.Length))
                    {
                        XmlElementPath path = XmlParser.GetActiveElementStartPath(text, text.Length);
                        if (path.Elements.Count > 0)
                        {                            
                            ICompletionData[] completionData = GetAttributeCompletionData(path);

                            //return only completion data for attributes that aren't already entered for this element
                            int startIndex = XmlParser.GetActiveElementStartIndex(textArea.Document.TextContent, textArea.Caret.Offset);
                            int endIndex = XmlParser.GetActiveElementEndIndex(textArea.Document.TextContent, textArea.Caret.Offset);

                            List<string> existingAttributes = new List<string>();

                            for (int i = startIndex; i < endIndex; i++)
                            {
                                string foundAttribute = XmlParser.GetAttributeNameAtIndex(textArea.Document.TextContent, i);

                                if (foundAttribute.IsNullOrEmpty())
                                    continue;

                                foreach (ICompletionData data in completionData)
                                {
                                    if (data.Text == foundAttribute && !existingAttributes.Contains(data.Text))
                                    {
                                        existingAttributes.Add(data.Text);
                                    }
                                }
                            }

                            XmlCompletionDataCollection toReturn = new XmlCompletionDataCollection();

                            foreach (ICompletionData data in completionData)
                            {
                                if (!existingAttributes.Contains(data.Text))
                                    toReturn.Add((XmlCompletionData)data);
                            }

                            return toReturn.ToArray();
                        }
                    }
                    break;

                case '\'':
                case '\"':

                    // Attribute value intellisense.
                    //if (XmlParser.IsAttributeValueChar(charTyped)) {
                    text = text.Substring(0, text.Length - 1);
                    string attributeName = XmlParser.GetAttributeName(text, text.Length);
                    if (attributeName.Length > 0)
                    {
                        XmlElementPath elementPath = XmlParser.GetActiveElementStartPath(text, text.Length);
                        if (elementPath.Elements.Count > 0)
                        {
                            preSelection = charTyped.ToString();
                            return GetAttributeValueCompletionData(elementPath, attributeName);
                            //		}
                        }
                    }
                    break;
            }

            return null;

        }


        ICompletionData[] GetChildElementCompletionData(XmlElementPath path)
        {
            ICompletionData[] completionData = null;

            XmlSchemaCompletionData schema = defaultSchemaCompletionData;
            if (schema != null)
            {
                completionData = schema.GetChildElementCompletionData(path);
            }

            return completionData;
        }

        ICompletionData[] GetAttributeCompletionData(XmlElementPath path)
        {
            ICompletionData[] completionData = null;

            XmlSchemaCompletionData schema = defaultSchemaCompletionData;
            if (schema != null)
            {
                completionData = schema.GetAttributeCompletionData(path);                                
            }

            return completionData;
        }

        ICompletionData[] GetAttributeValueCompletionData(XmlElementPath path, string name)
        {
            ICompletionData[] completionData = null;

            XmlSchemaCompletionData schema = defaultSchemaCompletionData;
            if (schema != null)
            {
                completionData = schema.GetAttributeValueCompletionData(path, name);
            }

            return completionData;
        }

        ImageList _ImageList;
        public ImageList ImageList
        {
            get
            {
                if (_ImageList == null)
                {
                    _ImageList = new ImageList();
                    //_ImageList.Images.Add(new System.Drawing.Bitmap(@"C:\element2.png"));

                }

                return _ImageList;
            }
        }

        public bool InsertAction(ICompletionData data, TextArea textArea, int insertionOffset, char key)
        {
            textArea.InsertString(data.Text);
            return false;
            //throw new Exception("The method or operation is not implemented.");
        }

        public string PreSelection
        {
            get
            {
                return "";
            }

            //get { throw new Exception("The method or operation is not implemented."); }
        }

        public CompletionDataProviderKeyResult ProcessKey(char key)
        {
            return CompletionDataProviderKeyResult.NormalKey;
            //throw new Exception("The method or operation is not implemented.");
        }

        #endregion
    }


}
